package com.baijia.authentication.handler.support;

import com.baijia.authentication.AccountType;
import com.baijia.authentication.BaijiaAccount;
import com.baijia.authentication.handler.exception.AuthenticationException;
import com.baijia.authentication.handler.exception.UrlMessageException;
import com.baijia.authentication.principal.Credentials;
import com.baijia.authentication.principal.credentials.JigouCascadeAccountCredentials;
import com.baijia.dao.OrgCascadeAccountDao;
import com.baijia.dao.po.OrgCascadeAccount;
import com.baijia.services.ServicesManager;
import com.baijia.services.impl.RegisteredServiceImpl;
import com.baijia.util.JigouPasswordUtil;
import com.baijia.util.RedisUtil;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Date;

/**
 * @title JigouAuthenticationHandler
 * @desc 机构子帐号认证具体逻辑
 * @author caoliang
 * @date 2015年12月8日
 * @version 1.0
 */
@Component
public class JigouCascadeAccountAuthenticationHandler extends AbstractPreAndPostProcessingAuthenticationHandler
    implements BaijiaAccount {

    private static String PREFIX = "Jigou_zizhanghao_";
    private static String LOGIN_FAIL_TIMES = "Jigou_zizhanghao_login_fail_times";
    private static String LOGIN_BAN_FLAG = "Jigou_zizhanghao_login_ban_flag";

    @Autowired
    private OrgCascadeAccountDao orgCascadeAccountDao;
    @Autowired
    private ServicesManager servicesManager;

    @Override
    protected boolean preAuthenticate(final Credentials credentials) throws AuthenticationException {
        JigouCascadeAccountCredentials jigouCredentials = (JigouCascadeAccountCredentials) credentials;
        String banFlag = RedisUtil.hGet(PREFIX + jigouCredentials.getUsername(), LOGIN_BAN_FLAG);
        if (StringUtils.isNotBlank(banFlag)) {
            if (BooleanUtils.toBoolean(banFlag)) {
                throw new UrlMessageException("banned", "账号已被锁定，请一小时后再试");
            }
        }
        return true;
    }

    @Override
    protected boolean doAuthentication(Credentials credentials) throws AuthenticationException {
        JigouCascadeAccountCredentials jigouCredentials = (JigouCascadeAccountCredentials) credentials;
        // 机构子帐号登录名为number#00这种形式
        String username = jigouCredentials.getUsername();
        String password = jigouCredentials.getPassword();
        jigouCredentials.setPassword("***");

        OrgCascadeAccount orgCascadeAccount = orgCascadeAccountDao.findByUsername(username);
        if (orgCascadeAccount == null) {
            return false;
        }

        try {
            if (jigouCredentials.isQrCode()) { // FIXME
                return true;
            }

            if (!JigouPasswordUtil.validatePassword(password, orgCascadeAccount.getPassword())) {
                return false;
            }

            // 未激活账户
            if (!BooleanUtils.toBoolean(orgCascadeAccount.getStatus())) {
                return false;
            }

            orgCascadeAccount.setLastLoginTime(new Date()); // FIXME useless code
            orgCascadeAccountDao.updateLastLoginTime(orgCascadeAccount.getId(), new Date());
        } catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
            logger.error("valid jigou cascade account password error! username:{}", username, e);
            return false;
        }

        return true;
    }

    @Override
    protected boolean postAuthenticate(final Credentials credentials, final boolean authenticated)
        throws AuthenticationException {
        JigouCascadeAccountCredentials jigouCredentials = (JigouCascadeAccountCredentials) credentials;
        if (!authenticated) {
            RedisUtil.incrBy(PREFIX + jigouCredentials.getUsername(), LOGIN_FAIL_TIMES, 1L);
            String failStr = RedisUtil.hGet(PREFIX + jigouCredentials.getUsername(), LOGIN_FAIL_TIMES);
            long failTimes = Long.valueOf(failStr);
            RegisteredServiceImpl service =
                (RegisteredServiceImpl) servicesManager.findServiceBy(jigouCredentials.getAccountType());
            if (failTimes >= service.getLoginMaxTimes()) {
                RedisUtil.hSet(PREFIX + jigouCredentials.getUsername(), LOGIN_BAN_FLAG, Boolean.TRUE.toString(),
                    service.getBanSeconds());
                // throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩0次");
            } else if (failTimes >= service.getLoginNoticeTimes()) {
                throw new UrlMessageException("badCredential", "账号或密码错误" + failTimes + "次，还剩"
                    + (service.getLoginMaxTimes() - failTimes) + "次");
            }
        } else {
            RedisUtil.del((PREFIX + jigouCredentials.getUsername()).getBytes());
        }

        return authenticated;
    }

    @Override
    public int getAccountType() {
        return AccountType.ORG_SUB_ACCT.getCode();
    }

    @Override
    public boolean supports(Credentials credentials) {
        return credentials instanceof JigouCascadeAccountCredentials;
    }

    public void setOrgCascadeAccountDao(OrgCascadeAccountDao orgCascadeAccountDao) {
        this.orgCascadeAccountDao = orgCascadeAccountDao;
    }

}
